﻿using System;
using System.IO;
using System.Text;
using System.Collections.Generic;

namespace Poi.NET.util
{
    /**
     * representation of a byte (8-bit) field at a fixed location within a
     * byte array
     *
     * @author Marc Johnson (mjohnson at apache dot org
     */
    public class ByteField : FixedField
    {
        private static readonly byte _default_value = 0;
        private byte _value;
        private readonly int _offset;

        /**
         * construct the ByteField with its offset into its containing
         * byte array and a default value of 0
         *
         * @param offset of the field within its byte array
         *
         * @exception ArrayIndexOutOfBoundsException if offset is negative
         */
        public ByteField(int offset) : this(offset, _default_value) { }

        /**
         * construct the ByteField with its offset into its containing
         * byte array and initialize its value
         *
         * @param offset of the field within its byte array
         * @param value the initial value
         *
         * @exception ArrayIndexOutOfBoundsException if offset is negative
         */
        public ByteField(int offset, byte value)
        {
            if (offset < 0)
            {
                throw new IndexOutOfRangeException(
                    "offset cannot be negative");
            }
            _offset = offset;
            set(value);
        }

        /**
         * Construct the ByteField with its offset into its containing
         * byte array and initialize its value from its byte array
         *
         * @param offset of the field within its byte array
         * @param data the byte array to read the value from
         *
         * @exception ArrayIndexOutOfBoundsException if the offset is not
         *            within the range of 0..(data.length - 1)
         */
        public ByteField(int offset, byte[] data)
            : this(offset)
        {
            readFromBytes(data);
        }

        /**
         * construct the ByteField with its offset into its containing
         * byte array, initialize its value, and write its value to its
         * byte array
         *
         * @param offset of the field within its byte array
         * @param value the initial value
         * @param data the byte array to write the value to
         *
         * @exception ArrayIndexOutOfBoundsException if the offset is not
         *            within the range of 0..(data.length - 1)
         */
        public ByteField(int offset, byte value, byte[] data)
            : this(offset, value)
        {
            writeToBytes(data);
        }

        /**
         * get the ByteField's current value
         *
         * @return current value
         */
        public byte get()
        {
            return _value;
        }

        /**
         * set the ByteField's current value
         *
         * @param value to be set
         */
        public void set(byte value)
        {
            _value = value;
        }

        /**
         * set the ByteField's current value and write it to a byte array
         *
         * @param value to be set
         * @param data the byte array to write the value to
         *
         * @exception ArrayIndexOutOfBoundsException if the offset is out
         *            of the byte array's range
         */
        public void set(byte value, byte[] data)
        {
            set(value);
            writeToBytes(data);
        }

        /* ********** START implementation of FixedField ********** */

        /**
         * set the value from its offset into an array of bytes
         *
         * @param data the byte array from which the value is to be read
         *
         * @exception ArrayIndexOutOfBoundsException if the offset is out
         *            of range of the bte array
         */
        public void readFromBytes(byte[] data)
        {
            _value = data[_offset];
        }

        /**
         * set the value from an InputStream
         *
         * @param stream the InputStream from which the value is to be
         *               read
         *
         * @exception BufferUnderrunException if there is not enough data
         *            available from the InputStream
         * @exception IOException if an IOException is thrown from reading
         *            the InputStream
         */
        public void readFromStream(StreamReader stream)
        {
            // TODO - are these ~Field used / necessary
            int ib = stream.Read();
            if (ib < 0)
            {
                throw new LittleEndian.BufferUnderrunException();
            }
            _value = (byte)ib;
        }

        /**
         * write the value out to an array of bytes at the appropriate
         * offset
         *
         * @param data the array of bytes to which the value is to be
         *             written
         *
         * @exception ArrayIndexOutOfBoundsException if the offset is out
         *            of the byte array's range
         */
        public void writeToBytes(byte[] data)
        {
            data[_offset] = _value;
        }

        /**
         * return the value as a String
         *
         * @return the value as a String
         */
        public String toString()
        {
            return _value.ToString();
        }

        /* **********  END  implementation of FixedField ********** */
    }   // end public class ByteField
}
